#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

#include "BlockQueue.hpp"

// 基于生产消费者模型的阻塞队列

void *Consumer(void *args)
{
    BlockQueue<int> *bq = (BlockQueue<int> *)args;
    while (1)
    {
        int data = 0;
        bq->Pop(&data);

        cout << "Consumer data: " << data << endl;
        // 消费者的消费速度也会影响生产者
    }
}
void *Producter(void *args)
{
    BlockQueue<int> *bq = (BlockQueue<int> *)args;
    while (1)
    {

        int data = rand() % 10 + 1;
        bq->Push(data);
        cout << "Producter data: " << data << endl;
        // 生产者休眠速度会影响消费者
        sleep(1);
    }
}
// 内置类型进行生产者消费模型交换
// 单个生产者-单个消费者
void test1()
{
    srand((uint16_t)time(nullptr) ^ getpid() ^ pthread_self());

    BlockQueue<int> *bq = new BlockQueue<int>();
    pthread_t c, p;
    pthread_create(&c, nullptr, Consumer, bq);
    pthread_create(&p, nullptr, Producter, bq);

    // 主线程进行等待！
    pthread_join(c, nullptr);
    pthread_join(p, nullptr);
}

template <class T1, class T2>
class ThreadObject
{
public:
    ThreadObject(BlockQueue<T1> *bq, const T2 &data)
        : _bq(bq), _thread_data(data)
    {
    }
    ~ThreadObject()
    {
    }

    T2 GetData() const { return _thread_data; }
    BlockQueue<T1> *GetBlockQueue() const { return _bq; }

private:
    BlockQueue<T1> *_bq;
    T2 _thread_data;
};

void *Consumers(void *args)
{
    ThreadObject<int, string> *obj = (ThreadObject<int, string> *)args;

    while (1)
    {
        int data = 0;
        obj->GetBlockQueue()->Pop(&data);

        cout << obj->GetData().c_str() << " data: " << data << endl;
        // 消费者的消费速度也会影响生产者
        sleep(1);
    }
}
void *Producters(void *args)
{
    ThreadObject<int, string> *obj = (ThreadObject<int, string> *)args;
    while (1)
    {
        int data = rand() % 10 + 1;
        obj->GetBlockQueue()->Push(data);
        cout << obj->GetData().c_str() << " data: " << data << endl;
        // 生产者休眠速度会影响消费者
    }
}
// 多生产者-多消费者
void test2()
{
    BlockQueue<int> *bq = new BlockQueue<int>();
    // 创键4个对象
    ThreadObject<int, string> *c_obj1 = new ThreadObject<int, string>(bq, "Consumer one");
    ThreadObject<int, string> *c_obj2 = new ThreadObject<int, string>(bq, "Consumer two");
    ThreadObject<int, string> *p_obj1 = new ThreadObject<int, string>(bq, "Producter one");
    ThreadObject<int, string> *p_obj2 = new ThreadObject<int, string>(bq, "Producter two");

    pthread_t c[2], p[2];

    pthread_create(&c[0], nullptr, Consumers, c_obj1);
    pthread_create(&c[1], nullptr, Consumers, c_obj2);
    pthread_create(&p[0], nullptr, Producters, p_obj1);
    pthread_create(&p[1], nullptr, Producters, p_obj2);

    pthread_join(c[0], nullptr);
    pthread_join(c[1], nullptr);
    pthread_join(p[0], nullptr);
    pthread_join(p[1], nullptr);
}

int main()
{
    test2();
}

/*
    为什么生产消费者模型是高效的?
    生产者需要生产数据，但是生产的数据从哪里来？
    消费者只是完成取走数据吗?

    首先，数据是从网络通信中获得，那么数据获取本身需要花费时间。
    其次，消费者取走数据后，也需要对数据进行处理，也需要花费时间。

    所以，生产消费模型在生产前数据传入，消费后数据处理结束后，就不用在进行等待，
    即生产者可以实现向“超市”输入数据的同时，也可以从数据处获得数据！

    那么得出：生成消费者模型高效并不体现在模型中的同步互斥，而是在生产端、消费端，
    各自可以实现并行操作！
*/
